package io.gitee.skyarthur1987.leporidae.delayqueue.autoconfigure;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import io.gitee.skyarthur1987.leporidae.utils.base.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.util.ErrorHandler;
import org.springframework.util.StringUtils;

import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 延迟队列任务拉取事件异步配置
 * Created by skyarthur on 2024/3/10
 */
@Configuration
@ConditionalOnProperty(name = "leporidae.delay-queue.poll-event-async.enable", havingValue = "true")
@Slf4j
public class DelayQueuePollEventAsyncConfig implements ApplicationContextAware {
    private ApplicationContext applicationContext;

    // 延时队列轮询时间发送模式，默认同步
    @Bean(name = "applicationEventMulticaster")
    public ApplicationEventMulticaster simpleApplicationEventMulticaster(
            @Autowired(required = false) @Qualifier("delayQueuePollEventErrorHandler") ErrorHandler errorHandler) {
        SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster();
        ThreadPoolProperties properties = delayQueuePollEventListenerPoolConfig();
        Executor taskExecutor = buildExecutor(properties);
        eventMulticaster.setTaskExecutor(taskExecutor);
        eventMulticaster.setErrorHandler(errorHandler);
        return eventMulticaster;
    }

    private Executor buildExecutor(ThreadPoolProperties properties) {
        String executorBeanName = properties.getExecutorBeanName();
        if (StringUtils.hasText(executorBeanName)) {
            log.info("ApplicationEventMulticaster executor: {}", executorBeanName);
            return applicationContext.getBean(executorBeanName, Executor.class);
        }
        log.info("Init new executor, config: {}", JsonUtils.toJson(properties));
        return new ThreadPoolExecutor(properties.getCorePoolSize(),
                properties.getMaximumPoolSize(),
                properties.getKeepAliveInSeconds(), TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(properties.getBlockingQueueSize()),
                new ThreadFactoryBuilder().setNameFormat(properties.getPoolName()).setDaemon(true).build(),
                new ThreadPoolExecutor.AbortPolicy());
    }

    @Bean
    @ConfigurationProperties(prefix = "leporidae.delay-queue.poll-event-async.pool")
    public ThreadPoolProperties delayQueuePollEventListenerPoolConfig() {
        return new ThreadPoolProperties();
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
